﻿using System;
using System.Collections.Generic;
using System.Data;
using NHDay_Linq.ConfORM;
using NHDay_Linq.Domain;
using NHDay_Linq.ProviderExtensions;
using NHibernate;
using NHibernate.ByteCode.LinFu;
using NHibernate.Cfg;
using NHibernate.Cfg.Loquacious;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Tool.hbm2ddl;
using NUnit.Framework;

namespace NHDay_Linq
{
	/// <summary>
	/// base class to initialize some structures needed for the tests
	/// </summary>
	public class TestBase
	{
		static TestBase()
		{
			// remember to initialize Log4Net or logging won't work.
			log4net.Config.XmlConfigurator.Configure();
		}

		private const string ConnectionString =
	        @"Data Source=localhost\SSql2008;Initial Catalog=NhDayLinq;Integrated Security=True;Pooling=False";

		protected Configuration NhConfig;
		protected ISessionFactory SessionFactory;
		protected List<Person> People;
		protected List<Pet> Pets;
		protected List<Child> Children;

		private static Configuration ConfigureNHibernate()
		{
			var configure = new Configuration();
			configure.SessionFactoryName("Demo");

			configure.Proxy(p =>
								{
									p.Validation = false;
									p.ProxyFactoryFactory<ProxyFactoryFactory>();
								});
			configure.DataBaseIntegration(db =>
			{
				db.Dialect<MyDialect>();
				db.Driver<SqlClientDriver>();
				db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
				db.IsolationLevel = IsolationLevel.ReadCommitted;
				db.ConnectionString = ConnectionString;
				db.Timeout = 10;
				db.HqlToSqlSubstitutions = "true 1, false 0, yes 'Y', no 'N'";
				
				// enabled for testing
				db.LogFormatedSql = true;
				// db.LogSqlInConsole = true;
				// db.AutoCommentSql = true;
			});

			// add linq extension, it's the same of:
			// .Properties.Add("linqtohql.generatorsregistry", "XXX.ExtendedLinqtoHqlGeneratorsRegistry,XXX");
			
			// configure.LinqToHqlGeneratorsRegistry<ExtendedLinqToHqlGeneratorsRegistry>();

			// configure.LinqToHqlGeneratorsRegistry<IsLikeLinqToHqlGeneratorsRegistry>();

			configure.LinqToHqlGeneratorsRegistry<AllLinqToHqlGeneratorsRegistry>();
//            configure.AddXmlString(@"<?xml version=""1.0"" encoding=""utf-8"" ?>
//            <hibernate-mapping xmlns=""urn:nhibernate-mapping-2.2"">
//               <import class=""NHDay_Linq.Dto.AdultDtoSample, NHDay_Linq""  />
//            </hibernate-mapping>");
			return configure;
		}

		/// <summary>
		/// Compile all the mappings as a whole
		/// </summary>
		/// <returns></returns>
		protected static HbmMapping GetMapping()
		{
			var domainMapper = new DomainMapper();
			
			var entities = new List<Type>();
			entities.AddRange(DomainMapperHelper.ApplyMappings<DomainMapping>(domainMapper.DomainDefinition, domainMapper.Mapper));

			return domainMapper.Mapper.CompileMappingFor(entities);
		}

		/// <summary>
		/// Compile all the mapping for each entity
		/// </summary>
		/// <returns></returns>
		protected static IEnumerable<HbmMapping> GetMappings()
		{
			var domainMapper = new DomainMapper();

			var entities = new List<Type>();
			entities.AddRange(DomainMapperHelper.ApplyMappings<DomainMapping>(domainMapper.DomainDefinition, domainMapper.Mapper));

			return domainMapper.Mapper.CompileMappingForEach(entities);
		}

		protected void CreateDatabase()
		{
		    CreateDatabaseSchema();

		    GenerateTestData();
		}

	    protected void CreateDatabaseSchema()
	    {
	        Assert.IsNotNull(NhConfig);

	        // clear up the database and create te test data
	        Assert.DoesNotThrow(() => new SchemaExport(NhConfig).Drop(false, true));

	        Assert.DoesNotThrow(() => new SchemaExport(NhConfig).Create(false, true));
	    }

	    [TestFixtureSetUp]
		public void Setup()
		{
			NhConfig = ConfigureNHibernate();
			// the mapping
			HbmMapping mappingDocument = GetMapping();
			Console.Write(mappingDocument.AsString());
			NhConfig.AddDeserializedMapping(mappingDocument, "Domain");


			// fix up the schema
			SchemaMetadataUpdater.QuoteTableAndColumns(NhConfig);
			// create a session factory
			Assert.DoesNotThrow(() => SessionFactory = NhConfig.BuildSessionFactory());
		}

		#region "test data creation functions"

		protected void GenerateTestData()
		{
			using(ISession session = SessionFactory.OpenSession())
			using (ITransaction tx = session.BeginTransaction())
			{
				Adult magnus = new Adult { FirstName = "Magnus", LastName = "Hedlund", Title = "Mr.", Long = 1, BirthDate = new DateTime(1974, 01, 01), Guid = Guid.NewGuid() };
				Adult terry = new Adult { FirstName = "Terry", LastName = "Adams", Title = "Mr.", Long = 2, BirthDate = new DateTime(1975, 02, 01), Guid = Guid.NewGuid() };
				Adult charlotte = new Adult { FirstName = "Charlotte", LastName = "Weiss", Title = "Ms.", Long = 3, BirthDate = new DateTime(1976, 03, 01), Guid = Guid.NewGuid() };
				Adult arlene = new Adult { FirstName = "Arlene", LastName = "Huff", Title = "Ms.", Long = 4, BirthDate = new DateTime(1977, 04, 01), Guid = Guid.NewGuid() };

				Child ronny = new Child { FirstName = "Ronny", LastName = magnus.LastName, Father = magnus, Mother = charlotte, Long = 5, Guid = Guid.NewGuid() };
				Child anna = new Child { FirstName = "Anna", LastName = terry.LastName, Father = terry, Mother = arlene, Long = 6, Guid = Guid.NewGuid() };

				Pet barley = new Pet { Name = "Barley", Owner = terry };
				Pet boots = new Pet { Name = "Boots", Owner = terry };
				Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
				Pet bluemoon = new Pet { Name = "Blue Moon", Owner = terry };
				Pet daisy = new Pet { Name = "Daisy", Owner = magnus };

				// add some pets too (to keep consistency between in memory mirror and the database objets)
				terry.Pets.Add(barley);
				terry.Pets.Add(boots);
				terry.Pets.Add(bluemoon);
				charlotte.Pets.Add(whiskers);
				magnus.Pets.Add(daisy);

				// add some items
				var item = (new Items())[0];
				magnus.RightHand = (new Items())[0];
				magnus.HeldItems.Add(item);

				People = new List<Person> { magnus, terry, charlotte, arlene };
				Pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };
				Children = new List<Child> {ronny, anna};

				foreach (var p in People)
					session.SaveOrUpdate(p);
				foreach (var pet in Pets)
					session.SaveOrUpdate(pet);
				foreach (var c in Children)
					session.SaveOrUpdate(c);

				tx.Commit();
			}
		}

		#endregion
	}
}
